package com.codegen;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.codegen.annotation.QueryOp;
import com.codegen.enums.QueryOpEnum;
import lombok.extern.slf4j.Slf4j;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * QueryWrapperUtil 工具类，支持所有实体类的 LambdaQueryWrapper 条件拼接，支持所有基本类型和集合类型的条件拼接
 */
@Slf4j
public final class QueryWrapperUtil {
    /**
     * 缓存TableInfo对象
     */
    private static final Map<String, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();

    /**
     * 私有构造方法，防止实例化工具类。
     */
    private QueryWrapperUtil() {
    }

    /**
     * 构建查询条件 - 支持Request对象
     *
     * @param wrapper LambdaQueryWrapper对象
     * @param request 请求对象
     * @param entityClass 实体类Class
     */
    public static <T, R> void buildQueryCondition(LambdaQueryWrapper<T> wrapper, R request, Class<T> entityClass) {
        if (ObjectUtil.isEmpty(request)) {
            return;
        }

        // 获取实体类的TableInfo
        final TableInfo tableInfo = getTableInfo(entityClass);
        if (tableInfo == null) {
            log.error("无法获取实体类 {} 的TableInfo", entityClass.getName());
            return;
        }

        // 遍历请求对象的字段
        for (Field field : request.getClass().getDeclaredFields()) {
            try {
                final QueryOp op = field.getAnnotation(QueryOp.class);
                if (op != null) {
                    // 查找对应的TableFieldInfo
                    final TableFieldInfo tableFieldInfo = findTableFieldInfo(tableInfo, field.getName());
                    if (tableFieldInfo != null) {
                        // 使用TableFieldInfo创建条件
                        final Object value = getFieldValue(request, field.getName());
                        if (ObjectUtil.isNotEmpty(value)) {
                            addConditionByFieldInfo(wrapper, value, tableFieldInfo, op.value());
                        }
                    }
                }
            } catch (Exception e) {
                log.error("构建查询条件失败: {}", e.getMessage());
            }
        }
    }

    /**
     * 安全地获取字段值
     *
     * @param target 目标对象
     * @param fieldName 字段名
     * @return 字段值
     */
    private static Object getFieldValue(Object target, String fieldName) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(fieldName, target.getClass());
            Method readMethod = pd.getReadMethod();
            if (readMethod != null) {
                return readMethod.invoke(target);
            }
        } catch (Exception e) {
            log.error("获取字段值失败: {}", e.getMessage());
        }
        return null;
    }

    /**
     * 获取实体类的TableInfo
     * @param entityClass 实体类Class
     * @return TableInfo对象
     */
    private static TableInfo getTableInfo(Class<?> entityClass) {
        return TABLE_INFO_CACHE.computeIfAbsent(entityClass.getName(), k -> TableInfoHelper.getTableInfo(entityClass));
    }

    /**
     * 查找TableFieldInfo
     * @param tableInfo TableInfo对象
     * @param fieldName 字段名
     * @return TableFieldInfo对象
     */
    private static TableFieldInfo findTableFieldInfo(TableInfo tableInfo, String fieldName) {
        return tableInfo.getFieldList().stream()
                .filter(field -> field.getProperty().equals(fieldName))
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据字段名添加条件
     */
    private static <T> void addConditionByFieldInfo(LambdaQueryWrapper<T> wrapper, Object value,
                                                   TableFieldInfo tableFieldInfo, QueryOpEnum op) {
        try {
            // 处理空值条件
            if (handleNullCondition(wrapper, tableFieldInfo, op)) {
                return;
            }

            if (ObjectUtil.isEmpty(value)) {
                return;
            }

            final String column = tableFieldInfo.getSqlSelect();
            
            // 处理不同类型的条件
            switch (op) {
                // 基本比较条件
                case EQ:
                case NE:
                case GT:
                case GE:
                case LT:
                case LE:
                    handleBasicCondition(wrapper, column, value, op);
                    break;
                // 模糊匹配条件
                case LIKE:
                case NOT_LIKE:
                case LIKE_LEFT:
                case LIKE_RIGHT:
                    handleLikeCondition(wrapper, column, value, op);
                    break;
                // 集合条件
                case IN:
                case NOT_IN:
                    handleCollectionCondition(wrapper, column, value, op);
                    break;
                // 范围条件
                case BETWEEN:
                case NOT_BETWEEN:
                    handleBetweenCondition(wrapper, column, value, op);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown QueryOp: " + op);
            }
        } catch (Exception e) {
            log.error("添加查询条件失败: {}", e.getMessage());
        }
    }

    /**
     * 处理空值条件
     */
    private static <T> boolean handleNullCondition(LambdaQueryWrapper<T> wrapper, TableFieldInfo tableFieldInfo, QueryOpEnum op) {
        if (op == QueryOpEnum.IS_NULL) {
            wrapper.apply(tableFieldInfo.getSqlSelect() + " IS NULL");
            return true;
        }
        if (op == QueryOpEnum.IS_NOT_NULL) {
            wrapper.apply(tableFieldInfo.getSqlSelect() + " IS NOT NULL");
            return true;
        }
        return false;
    }

    /**
     * 处理基本条件
     */
    private static <T> void handleBasicCondition(LambdaQueryWrapper<T> wrapper, String column, Object value, QueryOpEnum op) {
        switch (op) {
            case EQ:
                wrapper.apply(column + " = {0}", value);
                break;
            case NE:
                wrapper.apply(column + " <> {0}", value);
                break;
            case GT:
                wrapper.apply(column + " > {0}", value);
                break;
            case GE:
                wrapper.apply(column + " >= {0}", value);
                break;
            case LT:
                wrapper.apply(column + " < {0}", value);
                break;
            case LE:
                wrapper.apply(column + " <= {0}", value);
                break;
            default:
                break;
        }
    }

    /**
     * 处理LIKE条件
     */
    private static <T> void handleLikeCondition(LambdaQueryWrapper<T> wrapper, String column, Object value, QueryOpEnum op) {
        switch (op) {
            case LIKE:
                wrapper.apply(column + " LIKE CONCAT('%',{0},'%')", value);
                break;
            case NOT_LIKE:
                wrapper.apply(column + " NOT LIKE CONCAT('%',{0},'%')", value);
                break;
            case LIKE_LEFT:
                wrapper.apply(column + " LIKE CONCAT('%',{0})", value);
                break;
            case LIKE_RIGHT:
                wrapper.apply(column + " LIKE CONCAT({0},'%')", value);
                break;
            default:
                break;
        }
    }

    /**
     * 处理集合条件
     */
    private static <T> void handleCollectionCondition(LambdaQueryWrapper<T> wrapper, String column, Object value, QueryOpEnum op) {
        if (value instanceof Collection<?> coll && !coll.isEmpty()) {
                if (op == QueryOpEnum.IN) {
                    wrapper.apply(column + " IN ({0})", coll);
                } else {
                    wrapper.apply(column + " NOT IN ({0})", coll);
                }
            }

    }

    /**
     * 处理BETWEEN条件
     */
    private static <T> void handleBetweenCondition(LambdaQueryWrapper<T> wrapper, String column, Object value, QueryOpEnum op) {
        if (value instanceof List<?> list && list.size() == 2) {
                if (op == QueryOpEnum.BETWEEN) {
                    wrapper.apply(column + " BETWEEN {0} AND {1}", list.get(0), list.get(1));
                } else {
                    wrapper.apply(column + " NOT BETWEEN {0} AND {1}", list.get(0), list.get(1));
                }
            }

    }
}
